package demo.config;
import demo.handler.SparklrUserApprovalHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
/**
 * @author Peter Schneider-Manzell
 */
@Slf4j
@Configuration
@EnableAuthorizationServer
@EnableWebMvcSecurity
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
  private static final String DUMMY_RESOURCE_ID = "dummy";
  @Autowired
  @Qualifier("authenticationManagerBean")
  private AuthenticationManager authenticationManager;
  @Value("${default.redirect:http://localhost:8080/tonr2/sparklr/redirect}")
  private String defaultRedirectUri;
  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints
      //
      .tokenStore(tokenStore())
      //
      .tokenEnhancer(jwtTokenEnhancer())
      //
      .authenticationManager(authenticationManager);
  }
  @Bean
  public TokenStore tokenStore() {
    return new JwtTokenStore(jwtTokenEnhancer());
  }
  @Bean
  protected JwtAccessTokenConverter jwtTokenEnhancer() {
    KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(
      //
      new ClassPathResource("jwt-test.jks"),
      //
      "testpass".toCharArray());
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt-test"));
    return converter;
  }
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
      //
      .withClient("tonr").resourceIds(DUMMY_RESOURCE_ID).authorizedGrantTypes("authorization_code", "implicit").authorities("ROLE_CLIENT").scopes("read", "write").autoApprove(true).secret("secret").and()
      //
      .withClient("tonr-with-redirect").resourceIds(DUMMY_RESOURCE_ID).authorizedGrantTypes("authorization_code", "implicit").authorities("ROLE_CLIENT").scopes("read", "write").autoApprove(true).secret("secret").redirectUris(defaultRedirectUri).and()
      //
      .withClient("my-client-with-registered-redirect").resourceIds(DUMMY_RESOURCE_ID).authorizedGrantTypes("authorization_code", "client_credentials").authorities("ROLE_CLIENT").scopes("read", "trust").autoApprove(true).redirectUris("http://anywhere?key=value").and()
      //
      .withClient("my-trusted-client").authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit").authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT").scopes("read", "write", "trust").autoApprove(true).accessTokenValiditySeconds(60).and()
      //
      .withClient("my-trusted-client-with-secret").authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit", "client_credentials").authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT").scopes("read", "write", "trust").secret("somesecret").autoApprove(true).and()
      //
      .withClient("my-less-trusted-client").authorizedGrantTypes("authorization_code", "implicit").authorities("ROLE_CLIENT").scopes("read", "write", "trust").autoApprove(true).and()
      //
      .withClient("my-less-trusted-autoapprove-client").authorizedGrantTypes("implicit").authorities("ROLE_CLIENT").scopes("read", "write", "trust").autoApprove(true);
  }
  @Override
  public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    oauthServer.realm("sparklr2/client");
  }
  protected static class Stuff {
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private TokenStore tokenStore;
    @Bean
    public ApprovalStore approvalStore() throws Exception {
      TokenApprovalStore store = new TokenApprovalStore();
      store.setTokenStore(tokenStore);
      return store;
    }
    @Bean
    @Lazy
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    public SparklrUserApprovalHandler userApprovalHandler() throws Exception {
      SparklrUserApprovalHandler handler = new SparklrUserApprovalHandler();
      handler.setApprovalStore(approvalStore());
      handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
      handler.setClientDetailsService(clientDetailsService);
      handler.setUseApprovalStore(true);
      return handler;
    }
  }
}
